package com.heima.wemedia.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.heima.common.aliyun.GreenImageUploadScan;
import com.heima.common.aliyun.GreenTextScan;
import com.heima.common.delayTask.RedisDelayedQueueUtil;
import com.heima.common.enums.AppHttpCodeEnum;
import com.heima.common.exception.LeadException;
import com.heima.common.tess4j.Tess4jClient;
import com.heima.common.util.BeanHelper;
import com.heima.common.util.JsonUtils;
import com.heima.common.util.SensitiveWordUtil;
import com.heima.feignclient.client.ArticleFeign;
import com.heima.file.service.MinioService;
import com.heima.model.media.dtos.WmNewsResultDTO;
import com.heima.wemedia.entity.WmNews;
import com.heima.wemedia.entity.WmSensitive;
import com.heima.wemedia.service.WmNewsAuditService;
import com.heima.wemedia.service.WmNewsService;
import com.heima.wemedia.service.WmSensitiveService;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.units.qual.A;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.imageio.ImageIO;
import javax.swing.plaf.synth.SynthTextAreaUI;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
public class WmNewsAuditServiceImpl implements WmNewsAuditService {

    @Lazy //实现懒加载
    @Autowired
    private WmNewsService wmNewsService;
    @Autowired
    private ArticleFeign articleFeign;
    @Autowired
    private RedisDelayedQueueUtil delayedQueueUtil;

    @Override
    @GlobalTransactional(rollbackFor = Exception.class)
    @Async  //使用异步执行当前方法
    public void auditWmNews(WmNews wmNews) {
        log.info("开始审核文章");
//        判断状态是否待审核
        if(wmNews.getStatus()!=1){
            log.info("不是待审核状态，结束");
            return ;
        }
        Integer wmNewsId = wmNews.getId();
//        获取文章中的文本和图片 ,key - text  ,key - image
        Map<String,Object> map = getTextAndImage(wmNews);
        String text = map.get("text").toString();
        List<byte[]> imageList = (List<byte[]>)map.get("image");
//        阿里云文本审核
        boolean b = checkText(text,wmNewsId);
        if(!b){
            log.error("阿里云审核文本不通过");
            return ;
            //throw new LeadException(AppHttpCodeEnum.TEXT_ILLEGAL);
        }
//        阿里云图片审核
        boolean b1 = checkImage(imageList,wmNewsId);
        if(!b1){
            log.error("阿里云审核图片不通过");
            return ;
            //throw new LeadException(AppHttpCodeEnum.PARAM_IMAGE_ILLEGAL);
        }
//        敏感词审核
        boolean b2 = checkSensitive(text,wmNewsId);
        if(!b2){
            log.error("敏感词检测不通过");
            return ;
            //throw new LeadException(AppHttpCodeEnum.PARAM_IMAGE_ILLEGAL);
        }
        if(!CollectionUtils.isEmpty(imageList)){
            boolean b3 = true;
            for (byte[] bytes : imageList) {
                b3 = handleImageSensitiveScan(bytes,wmNewsId);
                if(!b3){
                    log.error("图片敏感词检测不通过");
                    break ;
                }
            }
            if(!b3){
                log.error("图片敏感词检测不通过");
                return ;
            }
        }
//        根据id 修改文章状态， 1-> 8
        updateWmNewsStaus(wmNewsId,8,null,null);
//        如果有定时发布，并且没有到发布时间，不要发布，直接结束
        if(wmNews.getPublishTime() != null &&
                System.currentTimeMillis() < wmNews.getPublishTime().getTime()){
            log.info("没有到发布时间，放入延迟任务");
//            计算延迟时间，用 发布时间 -  当前时间
            long delay = wmNews.getPublishTime().getTime() - System.currentTimeMillis();
//            放入延迟任务
            delayedQueueUtil.addQueue(wmNewsId,delay, TimeUnit.MILLISECONDS,"wm.news.pub");
            return ;
        }
//        对象转换
        WmNewsResultDTO newsResultDTO = BeanHelper.copyProperties(wmNews, WmNewsResultDTO.class);
//        发布文章,远程调用article服务
        publishWmNews(newsResultDTO);

    }

    /**
     * 发布文章
     * @param newsResultDTO
     */
    @Override
    public void publishWmNews(WmNewsResultDTO newsResultDTO) {
//        自媒体文章id
        Integer wmNewsId = newsResultDTO.getId();
        if(StringUtils.isBlank(newsResultDTO.getTitle())){
//            如果title是空，说明是 定时发布调用
//            根据id 查询文章内容
            WmNews wmNews = wmNewsService.getById(wmNewsId);
            newsResultDTO = BeanHelper.copyProperties(wmNews,WmNewsResultDTO.class);
        }
        try {
            Long articleId = articleFeign.saveArticle(newsResultDTO);
//        修改文章状态，改成-9
            updateWmNewsStaus(wmNewsId,9,null,articleId);
        }catch (Exception e){
            log.error("远程调用article保存文章，失败");
            e.printStackTrace();
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }


    @Autowired
    private WmSensitiveService wmSensitiveService;
    /**
     *  敏感词审核
     * @param text
     * @param wmNewsId
     * @return
     */
    private boolean checkSensitive(String text, Integer wmNewsId) {

//        查询所有的敏感词
        List<WmSensitive> wmSensitiveList = wmSensitiveService.list();
//        只需要敏感词
        List<String> sensitiveWord = wmSensitiveList.stream().
                map(WmSensitive::getSensitives).
                collect(Collectors.toList());
//        调用工具类检测敏感词
        Map<String, Integer> map = SensitiveWordUtil.matchWords(sensitiveWord, text);
        if(CollectionUtils.isEmpty(map)){
            log.info("敏感词检测成功");
            return true;
        }
        String reason = map.keySet().stream().collect(Collectors.joining(","));
        log.info("reason=={}",reason);
//        修改文章状态
        updateWmNewsStaus(wmNewsId,2,reason,null);
        return false;
    }

    @Autowired
    private GreenImageUploadScan imageUploadScan;

    /**
     * 阿里云检测图片
     * @param imageList
     * @param wmNewsId
     * @return
     */
    private boolean checkImage(List<byte[]> imageList, Integer wmNewsId) {
        if(CollectionUtils.isEmpty(imageList)){
            log.info("imagelist为空，停止检测");
            return true;
        }
//      调用阿里云图片审核接口
        try {
            Map<String, String> map = imageUploadScan.imageScan(imageList);
            String suggestion = map.get("suggestion");
            String label = map.get("label");
            if("pass".equals(suggestion)){
                log.info("阿里云图片审核通过");
                return true;
            }else if("review".equals(suggestion)){
                log.error("阿里云图片审核不确定，需要人工");
                this.updateWmNewsStaus(wmNewsId,3,label,null);
                return false;
            }else {
                log.error("阿里云图片审核不通过！！");
                this.updateWmNewsStaus(wmNewsId,2,label,null);
                return false;
            }
        }catch (Exception e){
            log.error("阿里云图片审核失败");
            e.printStackTrace();
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }

    @Autowired
    private GreenTextScan textScan;
    /**
     * 阿里云审核文本
     * @param text
     * @return
     */
    private boolean checkText(String text,Integer wmNewsId) {
        if(StringUtils.isBlank(text)){
            log.error("text 内容 为空，停止检测");
            throw new LeadException(AppHttpCodeEnum.TEXT_ILLEGAL);
        }
//      调用阿里云文本审核接口
        try {
            Map<String, String> map = textScan.greenTextScan(text);
            String suggestion = map.get("suggestion");
            String reason = map.get("reason");
            if("pass".equals(suggestion)){
                log.info("阿里云文本审核通过");
                return true;
            }else if("review".equals(suggestion)){
                log.error("阿里云审核不确定，需要人工");
                this.updateWmNewsStaus(wmNewsId,3,reason,null);
                return false;
            }else {
                log.error("阿里云审核不通过！！");
                this.updateWmNewsStaus(wmNewsId,2,reason,null);
                return false;
            }
        }catch (Exception e){
            log.error("阿里云文本审核失败");
            e.printStackTrace();
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }



    @Autowired
    private Tess4jClient tess4jClient;

    /**
     * 从图片中获取文字进行敏感词检测
     */
    private boolean handleImageSensitiveScan(byte[] bytes,Integer wmNewsId){
        try {
            //图片识别文字审核---begin-----
            //从byte[]转换为butteredImage
            ByteArrayInputStream in = new ByteArrayInputStream(bytes);
            BufferedImage imageFile = ImageIO.read(in);
            //识别图片的文字
            String result = tess4jClient.doOCR(imageFile);
            //审核是否包含自管理的敏感词
            boolean isSensitive = checkSensitive(result, wmNewsId);
            return isSensitive;
        }catch (Exception e){
            e.printStackTrace();
            return false;
        }
    }
    @Autowired
    private MinioService minioService;
    /**
     * 获取文章中的文本和图片 ,key - text  ,key - image
     * @param wmNews
     * @return
     */
    private Map<String, Object> getTextAndImage(WmNews wmNews) {
//        文本包括  title、content、labels，获取的是字符串
        StringBuilder stringBuilder = new StringBuilder();
        ////        图片包括  content、images，获取的是List<byte[]>
        List<byte[]> imageList = new ArrayList<>();
        stringBuilder.append(wmNews.getTitle());
        if(StringUtils.isNotBlank(wmNews.getLabels())){
            stringBuilder.append(",").append(wmNews.getLabels());
        }

        try {
//        解析content
            List<Map<String, Object>> list = JsonUtils.nativeRead(wmNews.getContent(), new TypeReference<List<Map<String, Object>>>() {
            });
            for (Map<String, Object> map : list) {
                String type = map.get("type").toString();
                String value = map.get("value").toString();
                if (type.equals("text")) {
                    stringBuilder.append(",").append(value);
                } else {
//                获取图片url
                    if (StringUtils.isNotBlank(value)) {
//                  根据图片的url，到minio下载图片，得到byte[]
                        byte[] bytes = minioService.downLoadFile(value);
                        imageList.add(bytes);
                    }
                }
            }
            if (StringUtils.isNotBlank(wmNews.getImages())) {
                String[] urls = wmNews.getImages().split(",");
                for (String url : urls) {
//                从minio下载封面图片的字节
                    byte[] bytes = minioService.downLoadFile(url);
                    imageList.add(bytes);
                }
            }
        }catch (Exception e){
            log.error("图片从minio下载失败");
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
//        返回map结果

        Map<String,Object> map = new HashMap<>();
        map.put("text",stringBuilder.toString());
        map.put("image",imageList);
        return map;
    }

    /**
     * 修改文章状态
     * @param wmNewsId
     * @param status  2- 审核不同通过 3- 待人工审核 8-审核通过
     * @param reason   2和3 的时候原因
     */
    private void updateWmNewsStaus(Integer wmNewsId, int status, String reason,Long articleId) {
        WmNews wmNews = new WmNews();
        wmNews.setId(wmNewsId);
        wmNews.setStatus(status);
        if(status == 2 || status==3) {
            wmNews.setReason(reason);
        }
        if(articleId != null){
            wmNews.setArticleId(articleId);
        }
        boolean b = wmNewsService.updateById(wmNews);
        if(!b){
            log.error("更新文章状态失败，wmNewsId={}",wmNewsId);
            throw new LeadException(AppHttpCodeEnum.SERVER_ERROR);
        }
    }
}
